| Tip |
| Despite the fact that FIR filters may have linear phase, they may require too many computations to be implemented, and are not appropriate for some applications when hardware is limited. IIR filters can be used in these cases as they require fewer computations than FIR filters. However, IIR filters usually do not have linear phase or it is very difficult to get a linear phase. A pesar del hecho de que los filtros FIR tiene una fase lineal, estos requieren demasiadas operaciones para ser implementados en algunas aplicaciones donde el hardware es limitado. Los filtros IIR pueden ser usados en estos casos ya que estos requieres menos número de operaciones que los filtros FIR. Sin embargo, los filtros IIR usualmente no tienen una fase lineal o es muy difícil conseguir una fase lineal. |
Butterworth low pass filter |
| The magnitude response of this type of filters is maximally flat in the pass band. For a continuous-time Butterworth low pass filter, magnitude-squared function is as shown below. The poles of this type of filter that lie on the left part of the s-plane are shown in the same figure. In order to achieve a stable and causal filter, the poles on the left-part of the s-plane must be chosen. La respuesta en amplitud de este tipo de filtros es plana al máximo en la banda de paso. Para un filtro pasa bajas de Butterworth en tiempo-continuo, el cuadrado de la función de amplitud es como se muestra debajo. Los polos de este tipo de filtros que caen en la parte izquierda del plano-s se muestran en la misma figura. A fin de conseguir un filtro causal y estable, los polos en la parte izquierda del plano-s deben de escogerse. |

| Problem 1 |
| Show that H(s) is transformed in H(z) as shown when the bilinear transformation is used. Demostrar que H(s) se transforma en H(z) como se muestra cuando se usa la transformada bilineal. |

| Problem 2 |
| To transform a low-pass filter to a high-pass filter, the transformation shown below is used. Show that H(s) is transformed in H(z) as shown when this transformation is applied. Para transformar un filtro pasa-bajas a un filtro pasa-altas, la transformación mostrada debajo es usada. Demostrar que H(s) se transforma en H(z) como se muestra cuando se aplica esta transformación. |

| Problem 3 |
Create a Wintempla dialog application called Butterworth to design an IIR filter using a Butterworth approximation. Open Wintempla to edit the GUI. Using the Show All Controls in Toolbox from the toolbar insert a DAC control as shown below (on the events tabs, be sure all events are unselected). After creating the project open the stdafx.h file and remove the comments from the line #define WIN_DAC_ADC_SUPPORT. The Math::IIRButterworth class uses a bi-quadratic structure for the filter implementation as shown in the system function below. Cree una aplicación de dialogo de Wintempla llamada Butterworth para diseñar un filtro IIR usando una aproximación de Butterworth. Abra Wintempla y edite la GUI. Usando the Show All Controls in Toolbox desde la barra de herramientas inserta un control DAC como se muestra debajo (en la pestaña de eventos, asegúrate de que todos los eventos estén deseleccionado). Después de crear el proyecto abra el archivo stdafx.h y remueva los comentarios de la línea #define WIN_DAC_ADC_SUPPORT. La clase Math::IIRButterworth usa una estructura bi-cuadrática para la implementación del filtro como se muestra en la función del sistema de abajo. |


| Butterworth.h |
| #pragma once //______________________________________ Butterworth.h #include "Resource.h" class Butterworth: public Win::Dialog, public Mm::IAudioOut { public: Butterworth() { } ~Butterworth() { } const int freqResolution = 1000; const double maxFreqHz = 10000.0; const int numPoints = 2048; valarray<double> freqRad; const double sampFreqHz = 44100.0; const size_t numSamples = 4096; Mm::WaveFile waveFile; IIR::ButterworthFilter filter; void RefreshGraphs(); //______________________________________________________________ Mm::IAudioOut void AudioOutStarted(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution); void AudioOutData(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution, WAVEHDR* waveHdr); void AudioOutStopped(); protected: . . . }; |
| Butterworth.cpp |
| . . . void Butterworth::Window_Open(Win::Event& e) { //________________________________________________________ xyMagnitude xyMagnitude.CaptionX = L"Frequency (Hz)"; xyMagnitude.CaptionY = L"Gain (dB)"; xyMagnitude.MinX = 10.0; xyMagnitude.MaxX = maxFreqHz * 10.0; xyMagnitude.DivisionCountX = 4; xyMagnitude.SubDivisionCountX = 5; xyMagnitude.LogScaleX = true; xyMagnitude.MinY = -90.0; xyMagnitude.MaxY = 10.0; xyMagnitude.Graphs.Add(numPoints); const double deltaX = (sampFreqHz / 2) / (numPoints - 1); for (int i = 0; i < numPoints; i++) { xyMagnitude.Graphs[0][i].x = i * deltaX; xyMagnitude.Graphs[0][i].y = 0.0; } //________________________________________________________ xyGroupDelay xyGroupDelay.CaptionX = L"Frequency (Hz)"; xyGroupDelay.CaptionY = L"Group Delay (samples)"; xyGroupDelay.MinX = 10.0; xyGroupDelay.MaxX = maxFreqHz * 10.0; xyGroupDelay.DivisionCountX = 4; xyGroupDelay.SubDivisionCountX = 5; xyGroupDelay.LogScaleX = true; xyGroupDelay.MinY = 0.0; xyGroupDelay.MaxY = 40.0; xyGroupDelay.Graphs.Add(numPoints); for (int i = 0; i < numPoints; i++) { if (i == 0) { xyGroupDelay.Graphs[0][i].x = 0.000001; } else { xyGroupDelay.Graphs[0][i].x = i * deltaX; } xyGroupDelay.Graphs[0][i].y = 0.0; } //________________________________________________________ polarZeroPole polarZeroPole.Graphs.Add(); // Poles polarZeroPole.Graphs.Add(); // Zeros polarZeroPole.Graphs[0].Caption = L"Poles"; polarZeroPole.Graphs[0].Type = Win::Graph::cross; // polarZeroPole.Graphs[1].Caption = L"Zeros"; polarZeroPole.Graphs[1].Type = Win::Graph::circle; polarZeroPole.Graphs[1].Color = RGB(0, 180, 0); //________________________________________________________ sldCutFreq sldCutFreq.SetRange(1, freqResolution); sldCutFreq.Position = freqResolution / 2; tbxCutFreq.DoubleValue = maxFreqHz / 2.0; //________________________________________________________ sldStopFreq sldStopFreq.SetRange(1, freqResolution); sldStopFreq.Position = freqResolution / 2; tbxStopFreq.DoubleValue = maxFreqHz / 2.0; //________________________________________________________ sldStopGain sldStopGain.SetRange(-50, 0); // dB sldStopGain.Position = -25; // dB tbxStopGain.DoubleValue = -25.0; // dB //________________________________________________________ ddDevice const int count = ::waveOutGetNumDevs(); WAVEOUTCAPS woc; const int wsize = sizeof(WAVEOUTCAPS); for (int i = 0; i < count; i++) { if (::waveOutGetDevCaps(i, &woc, wsize) == MMSYSERR_NOERROR) { ddDevice.Items.Add(woc.szPname, i); } } ddDevice.SelectedIndex = 0; this->btPlay.Enabled = true; this->btStop.Enabled = false; radioLowPass.Checked = true; RefreshGraphs(); } void Butterworth::sldCutFreq_Hscroll(Win::Event& e) { const int position = sldCutFreq.GetPosition(); tbxCutFreq.DoubleValue = (position * maxFreqHz) / freqResolution; RefreshGraphs(); } void Butterworth::sldStopFreq_Hscroll(Win::Event& e) { const int position = sldStopFreq.GetPosition(); tbxStopFreq.DoubleValue = (position * maxFreqHz) / freqResolution; RefreshGraphs(); } void Butterworth::sldStopGain_Hscroll(Win::Event& e) { const int position = sldStopGain.GetPosition(); tbxStopGain.DoubleValue = position; RefreshGraphs(); } void Butterworth::radioLowPass_Click(Win::Event& e) { RefreshGraphs(); } void Butterworth::radioHighPass_Click(Win::Event& e) { RefreshGraphs(); } void Butterworth::RefreshGraphs() { const double cutFreq = (2.0 * M_PI * tbxCutFreq.DoubleValue) / sampFreqHz; const double stopFreq = (2.0 * M_PI * tbxStopFreq.DoubleValue) / sampFreqHz; const double stopGain = tbxStopGain.DoubleValue; if (radioLowPass.Checked == true) { if (filter.CreateLowPass(cutFreq, stopFreq, stopGain) == false) { tbxSystemFunction.Text = L"ERROR"; return; } } else { if (filter.CreateHighPass(cutFreq, stopFreq, stopGain) == false) { tbxSystemFunction.Text = L"ERROR"; return; } } double freqRads, gain; int i; //___________________________________________________ Gain for(i = 0; i < numPoints; i++) { freqRads = (2.0 * M_PI * xyMagnitude.Graphs[0][i].x) / sampFreqHz; gain = filter.GetMagnitude(freqRads); if (gain == 0) { xyMagnitude.Graphs[0][i].y = -200.0; } else { xyMagnitude.Graphs[0][i].y = 20.0*log10(gain); } } xyMagnitude.RefreshAll(); //___________________________________________________ Group Delay for(i=0; i< numPoints; i++) { freqRads = (2.0 * M_PI * xyGroupDelay.Graphs[0][i].x) / sampFreqHz; xyGroupDelay.Graphs[0][i].y = filter.GetGroupDelay(freqRads); } xyGroupDelay.AutoScaleY(true); //___________________________________________________ Poles double real1, imag1, real2, imag2; int j = 0; const int size = (int)filter.biquad.size(); polarZeroPole.Graphs[0].SetPointCount(size*2); for (i = 0; i < size; i++) { filter.biquad[i].GetPoles(real1, imag1, real2, imag2); polarZeroPole.Graphs[0][j].x = atan2(imag1, real1); polarZeroPole.Graphs[0][j].y = sqrt(real1*real1 + imag1*imag1); j++; polarZeroPole.Graphs[0][j].x = atan2(imag2, real2); polarZeroPole.Graphs[0][j].y = sqrt(real2*real2 + imag2*imag2); j++; } //___________________________________________________ Zeros j = 0; polarZeroPole.Graphs[1].SetPointCount(size*2); for (i = 0; i < size; i++) { filter.biquad[i].GetZeros(real1, imag1, real2, imag2); polarZeroPole.Graphs[1][j].x = atan2(imag1, real1); polarZeroPole.Graphs[1][j].y = sqrt(real1*real1 + imag1*imag1); j++; polarZeroPole.Graphs[1][j].x = atan2(imag2, real2); polarZeroPole.Graphs[1][j].y = sqrt(real2*real2 + imag2*imag2); j++; } polarZeroPole.Refresh(); //___________________________________________________ System Function const int N = (int)filter.biquad.size(); wstring text; wchar_t wtext[512]; for (int i = 0; i < N; i++) { _snwprintf_s(wtext, 512, _TRUNCATE, L"b0 = %.10f\r\nb1 = %.10f\r\nb2 = %.10f\r\na1 = %.10f\r\na2 = %.10f\r\n__________________\r\n", filter.biquad[i].b0, filter.biquad[i].b1, filter.biquad[i].b2, filter.biquad[i].a1, filter.biquad[i].a2); text += wtext; } tbxSystemFunction.Text = text; } void Butterworth::btStop_Click(Win::Event& e) { dacOutput.Stop(); } void Butterworth::btPlay_Click(Win::Event& e) { //________________________________________________________ 1. Prompt to the user to get the filename Win::FileDlg dlg; dlg.Clear(); dlg.SetFilter(L"Wave files (*.wav)\0*.wav\0\0", 0, L"wav"); if (dlg.BeginDialog(hWnd, L"Open", false) != TRUE) return; //________________________________________________________ 2. Get the Device ID LPARAM deviceID = WAVE_MAPPER; ddDevice.GetSelectedData(deviceID); //________________________________________________________ 3. Open the Wave File const wchar_t* error = waveFile.OpenForReading(dlg.GetFileNameFullPath()); if (error != NULL) { this->MessageBox(error, L"Butterworth", MB_OK | MB_ICONERROR); return; } //________________________________________________________ 4. Start the DAC const unsigned int numChannels = waveFile.GetNumChannels(); const unsigned int bitsResolution = waveFile.GetBitsResolution(); const unsigned int numBytes = (unsigned int)numSamples * numChannels * bitsResolution / 8; error = dacOutput.Start((unsigned int)deviceID, waveFile.GetSampFreqHz(), numChannels, bitsResolution, numBytes, this); if (error != NULL) { this->MessageBox(error, L"Butterworth", MB_OK | MB_ICONERROR); return; } } void Butterworth::AudioOutStarted(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution) { btPlay.Enabled = false; btStop.Enabled = true; EnableCloseButton(false); } void Butterworth::AudioOutData(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution, WAVEHDR* waveHdr) { waveHdr->dwBytesRecorded = waveFile.ReadData(waveHdr->lpData, waveHdr->dwBufferLength); Sys::Sample16* samples = (Sys::Sample16*)waveHdr->lpData; const size_t numSamples = waveHdr->dwBytesRecorded / 4; filter.ComputeOutput(samples, numSamples); } void Butterworth::AudioOutStopped() { btPlay.Enabled = true; btStop.Enabled = false; waveFile.Close(); EnableCloseButton(true); } |



| Problem 4 |
Create a Wintempla dialog application called Chebyshev to design an IIR filter using a Chebyshev approximation. Open Wintempla to edit the GUI. Using the Show All Controls in Toolbox from the toolbar insert a DAC control as shown below (on the events tabs, be sure all events are unselected). After creating the project open the stdafx.h file and remove the comments from the line #define WIN_DAC_ADC_SUPPORT. The code of this problem is very similar to the code of the previous problem; the main difference is the use the Math::IIRChebyshev class instead of the Math::IIRButterworth class.Cree una aplicación de dialogo de Wintempla llamada Chebyshev para diseñar un filtro IIR usando una aproximación de Chebyshev. Abra Wintempla y edite la GUI. Usando the Show All Controls in Toolbox desde la barra de herramientas inserta un control DAC como se muestra debajo (en la pestaña de eventos, asegúrate de que todos los eventos estén deseleccionado). Después de crear el proyecto abra el archivo stdafx.h y remueva los comentarios de la línea #define WIN_DAC_ADC_SUPPORT. El código de este problema es muy semejante al código del problema previo; la diferencia principal es usar la clase Math::IIRChebyshev en lugar de la clase Math::IIRButterworth. |


| Chebyshev.h |
| #pragma once //______________________________________ Chebyshev.h #include "Resource.h" class Chebyshev: public Win::Dialog, public Mm::IAudioOut { public: Chebyshev() { } ~Chebyshev() { } const int freqResolution = 1000; const double maxFreqHz = 10000.0; const int numPoints = 2048; valarray<double> freqRad; const double sampFreqHz = 44100.0; const size_t numSamples = 4096; Mm::WaveFile waveFile; IIR::ChebyshevFilter filter; void RefreshGraphs(); //______________________________________________________________ Mm::IAudioOut void AudioOutStarted(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution); void AudioOutData(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution, WAVEHDR* waveHdr); void AudioOutStopped(); protected: . . . }; |
| Chebyshev.cpp |
| . . . void Chebyshev::Window_Open(Win::Event& e) { //________________________________________________________ xyMagnitude xyMagnitude.CaptionX = L"Frequency (Hz)"; xyMagnitude.CaptionY = L"Gain (dB)"; xyMagnitude.MinX= 10.0; xyMagnitude.MaxX= maxFreqHz * 10.0; xyMagnitude.DivisionCountX = 4; xyMagnitude.SubDivisionCountX = 5; xyMagnitude.LogScaleX = true; xyMagnitude.MinY= -90.0; xyMagnitude.MaxY= 10.0; xyMagnitude.Graphs.Add(numPoints); const double deltaX = (sampFreqHz/2) /(numPoints -1); for(int i = 0; i < numPoints; i++) { xyMagnitude.Graphs[0][i].x = i*deltaX; xyMagnitude.Graphs[0][i].y = 0.0; } //________________________________________________________ xyGroupDelay xyGroupDelay.CaptionX = L"Frequency (Hz)"; xyGroupDelay.CaptionY = L"Group Delay (samples)"; xyGroupDelay.MinX= 10.0; xyGroupDelay.MaxX= maxFreqHz * 10.0; xyGroupDelay.DivisionCountX = 4; xyGroupDelay.SubDivisionCountX = 5; xyGroupDelay.LogScaleX = true; xyGroupDelay.MinY= 0.0; xyGroupDelay.MaxY= 40.0; xyGroupDelay.Graphs.Add(numPoints); for(int i = 0; i < numPoints; i++) { if (i == 0) { xyGroupDelay.Graphs[0][i].x = 0.000001; } else { xyGroupDelay.Graphs[0][i].x = i*deltaX; } xyGroupDelay.Graphs[0][i].y = 0.0; } //________________________________________________________ polarZeroPole polarZeroPole.Graphs.Add(); // Poles polarZeroPole.Graphs.Add(); // Zeros polarZeroPole.Graphs[0].Caption = L"Poles"; polarZeroPole.Graphs[0].Type = Win::Graph::cross; // polarZeroPole.Graphs[1].Caption = L"Zeros"; polarZeroPole.Graphs[1].Type = Win::Graph::circle; polarZeroPole.Graphs[1].Color = RGB(0, 180, 0); //________________________________________________________ sldCutFreq sldCutFreq.SetRange(1, freqResolution); sldCutFreq.Position = freqResolution /2; tbxCutFreq.DoubleValue = maxFreqHz /2.0; //________________________________________________________ sldStopFreq sldStopFreq.SetRange(1, freqResolution); sldStopFreq.Position = freqResolution /2; tbxStopFreq.DoubleValue = maxFreqHz /2.0; //________________________________________________________ sldStopGain sldStopGain.SetRange(-50, 0); // dB sldStopGain.Position = -25; // dB tbxStopGain.DoubleValue = -25.0; // dB //________________________________________________________ ddDevice const int count = ::waveOutGetNumDevs(); WAVEOUTCAPS woc; const int wsize = sizeof(WAVEOUTCAPS); for (int i = 0; i < count; i++) { if (::waveOutGetDevCaps(i, &woc, wsize) == MMSYSERR_NOERROR) { ddDevice.Items.Add(woc.szPname, i); } } ddDevice.SelectedIndex = 0; this->btPlay.Enabled = true; this->btStop.Enabled = false; radioLowPass.Checked = true; RefreshGraphs(); } void Chebyshev::sldCutFreq_Hscroll(Win::Event& e) { const int position = sldCutFreq.GetPosition(); tbxCutFreq.DoubleValue = (position * maxFreqHz)/ freqResolution; RefreshGraphs(); } void Chebyshev::sldStopFreq_Hscroll(Win::Event& e) { const int position = sldStopFreq.GetPosition(); tbxStopFreq.DoubleValue = (position * maxFreqHz) / freqResolution; RefreshGraphs(); } void Chebyshev::sldStopGain_Hscroll(Win::Event& e) { const int position = sldStopGain.GetPosition(); tbxStopGain.DoubleValue = position; RefreshGraphs(); } void Chebyshev::radioLowPass_Click(Win::Event& e) { RefreshGraphs(); } void Chebyshev::radioHighPass_Click(Win::Event& e) { RefreshGraphs(); } void Chebyshev::RefreshGraphs() { const double cutFreq = (2.0 * M_PI * tbxCutFreq.DoubleValue) / sampFreqHz; const double stopFreq = (2.0 * M_PI * tbxStopFreq.DoubleValue) / sampFreqHz; const double stopGain = tbxStopGain.DoubleValue; if (radioLowPass.Checked == true) { if (filter.CreateLowPass(cutFreq, 1.0, stopFreq, stopGain) == false) { tbxSystemFunction.Text = L"ERROR"; return; } } else { if (filter.CreateHighPass(cutFreq, 1.0, stopFreq, stopGain) == false) { tbxSystemFunction.Text = L"ERROR"; return; } } double freqRads, gain; int i; //___________________________________________________ Gain for(i = 0; i < numPoints; i++) { freqRads = (2.0 * M_PI * xyMagnitude.Graphs[0][i].x) / sampFreqHz; gain = filter.GetMagnitude(freqRads); if (gain == 0) { xyMagnitude.Graphs[0][i].y = -200.0; } else { xyMagnitude.Graphs[0][i].y = 20.0*log10(gain); } } xyMagnitude.RefreshAll(); //___________________________________________________ Group Delay for(i=0; i< numPoints; i++) { freqRads = (2.0 * M_PI * xyGroupDelay.Graphs[0][i].x) / sampFreqHz; xyGroupDelay.Graphs[0][i].y = filter.GetGroupDelay(freqRads); } xyGroupDelay.AutoScaleY(true); //___________________________________________________ Poles double real1, imag1, real2, imag2; int j = 0; const int size = (int)filter.biquad.size(); polarZeroPole.Graphs[0].SetPointCount(size*2); for (i = 0; i < size; i++) { filter.biquad[i].GetPoles(real1, imag1, real2, imag2); polarZeroPole.Graphs[0][j].x = atan2(imag1, real1); polarZeroPole.Graphs[0][j].y = sqrt(real1*real1 + imag1*imag1); j++; polarZeroPole.Graphs[0][j].x = atan2(imag2, real2); polarZeroPole.Graphs[0][j].y = sqrt(real2*real2 + imag2*imag2); j++; } //___________________________________________________ Zeros j = 0; polarZeroPole.Graphs[1].SetPointCount(size*2); for (i = 0; i < size; i++) { filter.biquad[i].GetZeros(real1, imag1, real2, imag2); polarZeroPole.Graphs[1][j].x = atan2(imag1, real1); polarZeroPole.Graphs[1][j].y = sqrt(real1*real1 + imag1*imag1); j++; polarZeroPole.Graphs[1][j].x = atan2(imag2, real2); polarZeroPole.Graphs[1][j].y = sqrt(real2*real2 + imag2*imag2); j++; } polarZeroPole.Refresh(); //___________________________________________________ System Function const int N = (int)filter.biquad.size(); wstring text; wchar_t wtext[512]; for (int i = 0; i < N; i++) { _snwprintf_s(wtext, 512, _TRUNCATE, L"b0 = %.10f\r\nb1 = %.10f\r\nb2 = %.10f\r\na1 = %.10f\r\na2 = %.10f\r\n__________________\r\n", filter.biquad[i].b0, filter.biquad[i].b1, filter.biquad[i].b2, filter.biquad[i].a1, filter.biquad[i].a2); text += wtext; } tbxSystemFunction.Text = text; } void Chebyshev::btStop_Click(Win::Event& e) { dacOutput.Stop(); } void Chebyshev::btPlay_Click(Win::Event& e) { //________________________________________________________ 1. Prompt to the user to get the filename Win::FileDlg dlg; dlg.Clear(); dlg.SetFilter(L"Wave files (*.wav)\0*.wav\0\0", 0, L"wav"); if (dlg.BeginDialog(hWnd, L"Open", false) != TRUE) return; //________________________________________________________ 2. Get the Device ID LPARAM deviceID = WAVE_MAPPER; ddDevice.GetSelectedData(deviceID); //________________________________________________________ 3. Open the Wave File const wchar_t* error = waveFile.OpenForReading(dlg.GetFileNameFullPath()); if (error != NULL) { this->MessageBox(error, L"Chebyshev", MB_OK | MB_ICONERROR); return; } //________________________________________________________ 4. Start the DAC const unsigned int numChannels = waveFile.GetNumChannels(); const unsigned int bitsResolution = waveFile.GetBitsResolution(); const unsigned int numBytes = (unsigned int)numSamples * numChannels * bitsResolution / 8; error = dacOutput.Start((unsigned int)deviceID, waveFile.GetSampFreqHz(), numChannels, bitsResolution, numBytes, this); if (error != NULL) { this->MessageBox(error, L"Chebyshev", MB_OK | MB_ICONERROR); return; } } void Chebyshev::AudioOutStarted(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution) { btPlay.Enabled = false; btStop.Enabled = true; EnableCloseButton(false); } void Chebyshev::AudioOutData(unsigned int sampFreqHz, unsigned int numChannels, unsigned int bitsResolution, WAVEHDR* waveHdr) { waveHdr->dwBytesRecorded = waveFile.ReadData(waveHdr->lpData, waveHdr->dwBufferLength); Sys::Sample16* samples = (Sys::Sample16*)waveHdr->lpData; const size_t numSamples = waveHdr->dwBytesRecorded / 4; filter.ComputeOutput(samples, numSamples); } void Chebyshev::AudioOutStopped() { btPlay.Enabled = true; btStop.Enabled = false; waveFile.Close(); EnableCloseButton(true); } |

| Problem 5 |
Create a Wintempla dialog application called Elliptic to design an IIR filter using an Elliptic approximation. After creating the project open the stdafx.h file and remove the comments from the line #define WIN_DAC_ADC_SUPPORT. Open Wintempla to edit the GUI. Using the Show All Controls in Toolbox from the toolbar insert a DAC control as shown below (on the events tabs, be sure all events are unselected). Use the IIR::EllipticFilter class. Remember that this class is designed for a bi-quadratic structure implementation and the order of the filter will always be even.Cree una aplicación de dialogo de Wintempla llamada Elliptic para diseñar un filtro IIR usando una aproximación Eliptica. Abra Wintempla y edite la GUI. Usando the Show All Controls in Toolbox desde la barra de herramientas inserta un control DAC como se muestra debajo (en la pestaña de eventos, asegúrate de que todos los eventos estén deseleccionado). Después de crear el proyecto abra el archivo stdafx.h y remueva los comentarios de la línea #define WIN_DAC_ADC_SUPPORT. Use la clase IIR::EllipticFilter. Recuerde que esta clase está diseñada para implementarse en una estructura bi-cuadrática y el orden del filtro será siempre par. |

| Problem 6 |
| Create a Wintempla dialog application called LinkPlay to play a wave file using two Linkwitz-Riley filters. Cree una aplicación de diálogo de Wintempla llamada LinkPlay para reproducir un archivo wave usando dos filtros Linkwitz-Riley. |
| Step A |
| Edit the stdafx.h file to activate the DAC and ADC by removing the comments of the shown line. Edite el archivo stdafx.h para activar el DAC y el ADC removiendo los comentarios de la línea mostrada. |
| stdafx.h |
| . . . //_________________________________________ MIDI, Audio Card DAC's and ADC's (or GDI Game for timers) //#define WIN_DAC_ADC_SUPPORT . . . |
| Step B |
| Use Wintempla to insert a Digital to Analog Converter (DAC). Once Wintempla is open, click the Show All Controls in Toolbox to show all controls. Set the name to dacOutput. In the Events tab, be sure all events are unselected. Insert a Signal View (be sure all events are unselected). Insert a button to Play and another one to Stop as shown. Insert a Drop Down List to select the output device. Then, insert a slider (with the Hscroll event), a textbox and a label) for the frequency. Finally, insert two labels and two sliders (with the Hscroll event) for the low frequency gain and the high frequency gain as shown. Use Wintempla para inserte un Digital to Analog Converter (DAC). Una vez que Wintempla se abre, haga clic en Show All Controls in Toolbox para mostrar todos los controles. Fije el nombre a dacOutput. En la pestaña de eventos, asegúrese que todos los eventos están deseleccionados. Inserte un Signal View (asegúrese que todos los eventos están deseleccionados). Inserte un botón para Reproducir y otro para Detener como se muestra. Inserte una Drop Down List para seleccionar el dispositivo de salida. Entonces, inserte un slider (con el evento Hscroll), una caja de texto y una etiqueta. Finalmente, inserte dos etiquetas y dos sliders (con el evento Hscroll) para la ganancia de la baja frecuencia y para la ganancia para la frecuencia alta como se muestra. |



| Step C |
| Edit the LinkPlay.h file and the LinkPlay.cpp file to implement the three functions of the Mm::IAudioOut interface (Observe the the LinkPlay class is derived from Mm::IAudioOut). Remember that an interface is used to pass a set of functions to another function or another object. Edite los archivos LinkPlay.h y LinkPlay.cpp para implementar las tres funciones de la interface Mm::IAudioOut (Observa que la clase LinkPlay se deriva de Mm::IAudioOut). Recuerde que una interface es usada para pasar un conjunto de funciones a otra función u objeto. |
| LinkPlay.h |
| #pragma once //______________________________________ LinkPlay.h #include "Resource.h" #define FREQ_RES 100 #define FREQ_MIN 80.0 #define FREQ_MAX 14000.0 class LinkPlay: public Win::Dialog, public Mm::IAudioOut { public: LinkPlay() { } ~LinkPlay() { Delete(); } void Delete(); double lowGain = 1.0; double highGain = 1.0; unsigned int sampFreqHz = 0; void CreateFilters(); //IIR::Biquad lowFilter; //IIR::Biquad highFilter; IIR::LinkwitzRileyFilter lowFilter; IIR::LinkwitzRileyFilter highFilter; Mm::WaveFile waveFile; Sys::Sample16* dataLow = nullptr; Sys::Sample16* dataHigh = nullptr; //______________________________________________________________ Mm::IAudioOut void AudioOutStarted(unsigned int samplesPerSec, unsigned int numChannels, unsigned int bitsResolution); void AudioOutData(unsigned int samplesPerSec, unsigned int numChannels, unsigned int bitsResolution, WAVEHDR* waveHdr); void AudioOutStopped(); protected: . . . }; |
| PlayLink.cpp |
| . . . void LinkPlay::Window_Open(Win::Event& e) { btStop.Enabled = false; //________________________________________________________ ddDevice const int count = ::waveOutGetNumDevs(); WAVEOUTCAPS woc; const int wsize= sizeof(WAVEOUTCAPS); for (int i = 0; i < count; i++) { if (::waveOutGetDevCaps(i, &woc, wsize) == MMSYSERR_NOERROR) { ddDevice.Items.Add(woc.szPname, i); } } ddDevice.SelectedIndex = 0; //________________________________________________________ sldCutFrequency sldCutFrequency.SetRange(0, FREQ_RES); sldCutFrequency.Position = FREQ_RES/2; tbxCutFrequency.DoubleValue = FREQ_MIN+ 0.5*(FREQ_MAX - FREQ_MIN); //________________________________________________________ sldHighFreqGain sldHighFreqGain.SetRange(-40, 0); sldHighFreqGain.Position = 0; //________________________________________________________ sldLowFreqGain sldLowFreqGain.SetRange(-40, 0); sldLowFreqGain.Position = 0; } void LinkPlay::CreateFilters() { const double cutFreqHz = tbxCutFrequency.DoubleValue; const double cutFreqRads = (2.0 * M_PI * cutFreqHz) / sampFreqHz; lowFilter.CreateLowPass(cutFreqRads); highFilter.CreateHighPass(cutFreqRads); //lowFilter.CreateLinkwitzRileyLowPass(cutFreqHz, sampFreqHz); //highFilter.CreateLinkwitzRileyHighPass(cutFreqHz, sampFreqHz); } void LinkPlay::sldCutFrequency_Hscroll(Win::Event& e) { const int position = sldCutFrequency.HasPositionChanged(); if (position < 0) return; tbxCutFrequency.DoubleValue = FREQ_MIN+ position*(FREQ_MAX - FREQ_MIN)/FREQ_RES; CreateFilters(); } void LinkPlay::btPlay_Click(Win::Event& e) { //________________________________________________________ 1. Prompt to the user to get the filename Win::FileDlg dlg; dlg.Clear(); dlg.SetFilter(L"Wave files (*.wav)\0*.wav\0\0", 0, L"wav"); if (dlg.BeginDialog(hWnd, L"Open", false) != TRUE) return; //________________________________________________________ 2. Get the Device ID LPARAM deviceID = WAVE_MAPPER; ddDevice.GetSelectedData(deviceID); //________________________________________________________ 3. Open the Wave File const wchar_t* error = waveFile.OpenForReading(dlg.GetFileNameFullPath()); if (error != NULL) { this->MessageBox(error, L"LinkPlay", MB_OK | MB_ICONERROR); return; } //________________________________________________________ 4. Start the DAC const unsigned int bufferSize = 16384; error = dacOutput.Start((unsigned int)deviceID, waveFile.GetSampFreqHz(), waveFile.GetNumChannels(), waveFile.GetBitsResolution(), bufferSize, this); if (error != NULL) { this->MessageBox(error, L"LinkPlay", MB_OK | MB_ICONERROR); return; } //________________________________________________________ 5. Memory allocation: dataLow and dataHigh Delete(); dataLow = new Sys::Sample16[bufferSize/4]; // four bytes per sample dataHigh = new Sys::Sample16[bufferSize / 4]; // four bytes per sample } void LinkPlay::btStop_Click(Win::Event& e) { dacOutput.Stop(); } void LinkPlay::AudioOutStarted(unsigned int samplesPerSec, unsigned int numChannels, unsigned int bitsResolution) { this->sampFreqHz = samplesPerSec; btPlay.Enabled = false; btStop.Enabled = true; ddDevice.Enabled = false; EnableCloseButton(false); CreateFilters(); svMain.Zoom = 0.1; svMain.Setup(samplesPerSec, numChannels, bitsResolution); } void LinkPlay::AudioOutData(unsigned int samplesPerSec, unsigned int numChannels, unsigned int bitsResolution, WAVEHDR* waveHdr) { int i; waveHdr->dwBytesRecorded = waveFile.ReadData(waveHdr->lpData, waveHdr->dwBufferLength); Sys::Sample16 *samples = (Sys::Sample16 *)waveHdr->lpData; const int numSamples = (waveHdr->dwBytesRecorded)/4; // four bytes per sample for (i = 0; i < numSamples; i++) { dataLow[i].channel_1 = (int16_t)(0.7 * lowGain * samples[i].channel_1); dataLow[i].channel_2 = (int16_t)(0.7 * lowGain * samples[i].channel_2); // dataHigh[i].channel_1 = (int16_t)(0.7 * highGain * samples[i].channel_1); dataHigh[i].channel_2 = (int16_t)(0.7 * highGain * samples[i].channel_2); } lowFilter.ComputeOutput(dataLow, numSamples); highFilter.ComputeOutput(dataHigh, numSamples); for (i = 0; i < numSamples; i++) { samples[i].channel_1 = (int16_t)(dataLow[i].channel_1 + dataHigh[i].channel_1); samples[i].channel_2 = (int16_t)(dataLow[i].channel_2 + dataHigh[i].channel_2); } svMain.RefreshFromDAC(waveHdr->lpData, waveHdr->dwBytesRecorded); } void LinkPlay::AudioOutStopped() { btPlay.Enabled = true; btStop.Enabled = false; ddDevice.Enabled = true; waveFile.Close(); EnableCloseButton(true); } void LinkPlay::sldHighFreqGain_Hscroll(Win::Event& e) { const int position = sldHighFreqGain.Position; highGain = pow(10.0, position/20.0); } void LinkPlay::sldLowFreqGain_Hscroll(Win::Event& e) { const int position = sldLowFreqGain.Position; lowGain = pow(10.0, position/20.0); } void LinkPlay::Delete() { if (dataLow != nullptr) { delete[] dataLow; dataLow = nullptr; } if (dataHigh != nullptr) { delete[] dataHigh; dataHigh = nullptr; } } |

| Problem 7 |
| Create a Wintempla dialog application called LowPassBoost to design several types of IIR filters. Edit the LowPassBoost.h file and the LowPassBoost.cpp file to implement the three functions of the Mm::IAudioOut interface (Observe the the LowPassBoost class is derived from Mm::IAudioOut). Cree una aplicación de dialogo de Wintempla llamada LowPassBoost para diseñar varios tipos de filtros IIR. Edite los archivos LowPassBoost.h y LowPassBoost.cpp para implementar las tres funciones de la interface Mm::IAudioOut (Observa que la clase LowPassBoost se deriva de Mm::IAudioOut). |
| LowPassBoost.h |
| #pragma once //______________________________________ LowPassBoost.h #include "Resource.h" #define POINT_COUNT 4096 class LowPassBoost: public Win::Window, public Mm::IAudioOut { public: LowPassBoost() { } ~LowPassBoost() { } unsigned int samplingFreqHz = 44100; void CreateFilter(); IIR::Biquad biquad; Mm::WaveFile waveFile; //______________________________________________________________ Mm::IAudioOut void AudioOutStarted(unsigned int samplesPerSec, unsigned int numChannels, unsigned int bitsResolution); void AudioOutData(unsigned int samplesPerSec, unsigned int numChannels, unsigned int bitsResolution, WAVEHDR* waveHdr); void AudioOutStopped(); void RefreshGraph(); const wchar_t * GetClassName(){return L"LowPassBoost";} . . . }; |
| LowPassBoost.cpp |
| . . . void LowPassBoost::Window_Open(Win::Event& e) { //________________________________________________________ xyMagnitude xyMagnitude.CaptionX = L"Frequency (Hz)"; xyMagnitude.CaptionY = L"Gain (dB)"; xyMagnitude.MinX= 10.0; // Hz xyMagnitude.MaxX= 100000.0; // Hz xyMagnitude.MinY= -50.0; // dB xyMagnitude.MaxY= 20.0; // dB xyMagnitude.SetLogScaleX(true); xyMagnitude.DivisionCountX = 4; xyMagnitude.DivisionCountY = 7; xyMagnitude.Graphs.Add(POINT_COUNT); const double deltaX = (samplingFreqHz / 2.0) /(POINT_COUNT - 1.0); for (int i=0; i<POINT_COUNT; i++) { xyMagnitude.Graphs[0][i].x = i*deltaX; xyMagnitude.Graphs[0][i].y = 0.0; } xyMagnitude.ColorMode = WIN_COLOR_MODE_DARK; xyMagnitude.Graphs[0].Color = RGB(0, 200, 0); xyMagnitude.TextColor = RGB(255, 255, 255); //________________________________________________________ sldCutFreq sldCutFreq.SetRange(30, 15000); sldCutFreq.Position = 100; tbxCutFreq.DoubleValue = sldCutFreq.Position; //________________________________________________________ sldGain sldGain.SetRange(-20, 20); sldGain.Position = 0; tbxGain.DoubleValue = sldGain.Position; //________________________________________________________ sldQ sldQ.SetRange(50, 200); sldQ.Position = 50; tbxQ.DoubleValue = 50.0 / 100.0; //________________________________________________________ polarZeroPole polarZeroPole.Graphs.Add(); // Poles polarZeroPole.Graphs.Add(); // Zeros polarZeroPole.Graphs[0].Caption = L"Poles"; polarZeroPole.Graphs[0].Type = Win::Graph::cross; // polarZeroPole.Graphs[1].Caption = L"Zeros"; polarZeroPole.Graphs[1].Type = Win::Graph::circle; polarZeroPole.Graphs[1].Color = RGB(0, 180, 0); //________________________________________________________ ddDevice const int count = ::waveOutGetNumDevs(); WAVEOUTCAPS woc; const int wsize = sizeof(WAVEOUTCAPS); for (int i = 0; i < count; i++) { if (::waveOutGetDevCaps(i, &woc, wsize) == MMSYSERR_NOERROR) { ddDevice.Items.Add(woc.szPname, i); } } ddDevice.SelectedIndex = 0; radioPeakingEQ.Checked = true; btStop.Enabled = false; RefreshGraph(); } void LowPassBoost::sldCutFreq_Hscroll(Win::Event& e) { int position = 0; if (sldCutFreq.GetPosition(position) == false) return; tbxCutFreq.DoubleValue = position; RefreshGraph(); } void LowPassBoost::sldGain_Hscroll(Win::Event& e) { int position = 0; if (sldGain.GetPosition(position) == false) return; tbxGain.DoubleValue = position; RefreshGraph(); } void LowPassBoost::sldQ_Hscroll(Win::Event& e) { int position = 0; if (sldQ.GetPosition(position) == false) return; tbxQ.DoubleValue = position/100.0; RefreshGraph(); } void LowPassBoost::RefreshGraph() { CreateFilter(); double freqHz, freqRad, gain; int i; //___________________________________________________ 1. Magnitude for (i = 0; i < POINT_COUNT; i++) { freqHz= xyMagnitude.Graphs[0][i].x; freqRad = 2.0*M_PI*freqHz/ samplingFreqHz; gain = biquad.GetMagnitude(freqRad); if (gain == 0.0) { xyMagnitude.Graphs[0][i].y = -200.0; } else { xyMagnitude.Graphs[0][i].y = 20.0*log10(gain); } } xyMagnitude.RefreshAll(); //___________________________________________________ 2. Poles double real1, imag1, real2, imag2; polarZeroPole.Graphs[0].SetPointCount(2); biquad.GetPoles(real1, imag1, real2, imag2); // Pole 1 polarZeroPole.Graphs[0][0].x = atan2(imag1, real1); polarZeroPole.Graphs[0][0].y = sqrt(real1*real1 + imag1*imag1); // Pole 2 polarZeroPole.Graphs[0][1].x = atan2(imag2, real2); polarZeroPole.Graphs[0][1].y = sqrt(real2*real2 + imag2*imag2); //___________________________________________________ 3. Zeros polarZeroPole.Graphs[1].SetPointCount(2); biquad.GetZeros(real1, imag1, real2, imag2); // Zero 1 polarZeroPole.Graphs[1][0].x = atan2(imag1, real1); polarZeroPole.Graphs[1][0].y = sqrt(real1*real1 + imag1*imag1); // Zero 2 polarZeroPole.Graphs[1][1].x = atan2(imag2, real2); polarZeroPole.Graphs[1][1].y = sqrt(real2*real2 + imag2*imag2); // polarZeroPole.Refresh(); } void LowPassBoost::btPlay_Click(Win::Event& e) { //________________________________________________________ 1. Prompt to the user to get the filename Win::FileDlg dlg; dlg.Clear(); dlg.SetFilter(L"Wave files (*.wav)\0*.wav\0\0", 0, L"wav"); if (dlg.BeginDialog(hWnd, L"Open", false) != TRUE) return; //________________________________________________________ 2. Get the Device ID LPARAM deviceID = WAVE_MAPPER; ddDevice.GetSelectedData(deviceID); //________________________________________________________ 3. Open the Wave File const wchar_t* error = waveFile.OpenForReading(dlg.GetFileNameFullPath()); if (error != NULL) { this->MessageBox(error, L"LowPassBoost", MB_OK | MB_ICONERROR); return; } //________________________________________________________ 4. Start the DAC error = dacOutput.Start((unsigned int)deviceID, waveFile.GetSampFreqHz(), waveFile.GetNumChannels(), waveFile.GetBitsResolution(), 16384, this); if (error != NULL) { this->MessageBox(error, L"LowPassBoost", MB_OK | MB_ICONERROR); return; } } void LowPassBoost::btStop_Click(Win::Event& e) { dacOutput.Stop(); } void LowPassBoost::AudioOutStarted(unsigned int samplesPerSec, unsigned int numChannels, unsigned int bitsResolution) { this->samplingFreqHz = samplesPerSec; btPlay.Enabled = false; btStop.Enabled = true; ddDevice.Enabled = false; EnableCloseButton(false); CreateFilter(); } void LowPassBoost::AudioOutStopped() { btPlay.Enabled = true; btStop.Enabled = false; ddDevice.Enabled = true; waveFile.Close(); EnableCloseButton(true); } void LowPassBoost::AudioOutData(unsigned int samplesPerSec, unsigned int numChannels, unsigned int bitsResolution, WAVEHDR* waveHdr) { waveHdr->dwBytesRecorded = waveFile.ReadData(waveHdr->lpData, waveHdr->dwBufferLength); Sys::Sample16* samples = (Sys::Sample16*)waveHdr->lpData; const int numSamples = (waveHdr->dwBytesRecorded) / 4; // four bytes per sample biquad.ComputeOutput(samples, numSamples); } void LowPassBoost::CreateFilter() { const double cutFreqHz = tbxCutFreq.DoubleValue; const double cutFreqRads = 2.0 * M_PI * cutFreqHz / samplingFreqHz; const double gaindB = tbxGain.DoubleValue; const double Q = tbxQ.DoubleValue; if (radioPeakingEQ.Checked) { biquad.CreatePeakingEQ(cutFreqRads, gaindB, Q, true); } else if (radioAllPass.Checked) { biquad.CreateAllPass(cutFreqRads, Q); } else if (radioShelvingLow.Checked) { biquad.CreateShelvingLow(cutFreqRads, gaindB, Q, true); } else if (radioShelvingHigh.Checked) { biquad.CreateShelvingHigh(cutFreqRads, gaindB, Q, true); } else if (radioBandpass.Checked) { biquad.CreateBandPass(cutFreqRads, Q, false); } else if (radioNotch.Checked) { biquad.CreateNotch(cutFreqRads, Q); } else if (radioLowpass.Checked) { biquad.CreateLowPass(cutFreqRads, Q); } else if (radioButterworthLP.Checked) { biquad.CreateButterworthLP(cutFreqRads); } else if (radioChebyshevLP.Checked) { biquad.CreateChebyshevLP(cutFreqRads, 1.0); } else if (radioEllipticLP.Checked) { biquad.CreateEllipticLP(cutFreqRads, cutFreqRads + 1.0, 1.0); } else if (radioHighpass.Checked) { biquad.CreateHighPass(cutFreqRads, Q); } else if (radioButterworthHP.Checked) { biquad.CreateButterworthHP(cutFreqRads); } else if (radioChebyshevHP.Checked) { biquad.CreateChebyshevHP(cutFreqRads, 1.0); } else if (radioEllipticHP.Checked) { biquad.CreateEllipticHP(cutFreqRads, cutFreqRads - 0.2, 1.0); } } void LowPassBoost::radioPeakingEQ_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioAllPass_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioShelvingLow_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioShelvingHigh_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioLowpass_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioHighpass_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioBandpass_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioNotch_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioButterworthHP_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioButterworthLP_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioChebyshevHP_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioChebyshevLP_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioEllipticLP_Click(Win::Event& e) { RefreshGraph(); } void LowPassBoost::radioEllipticHP_Click(Win::Event& e) { RefreshGraph(); } |













